package structure

import (
	"gitee.com/hongzhaomin/hzm-common-go/streams"
	"reflect"
)

type HzmTree[T comparable] interface {
	GetId() T
	GetParentId() T
	AddChildren(...HzmTree[T])
	//AddChildren([]HzmTree[T])
}

type BaseTree[T comparable] struct {
	Id       T            `json:"id"`
	ParentId T            `json:"parentId"`
	Children []HzmTree[T] `json:"children"`
}

var _ HzmTree[any] = (*BaseTree[any])(nil)

func (b *BaseTree[T]) GetId() T {
	return b.Id
}

func (b *BaseTree[T]) GetParentId() T {
	return b.ParentId
}

func (b *BaseTree[T]) AddChildren(children ...HzmTree[T]) {
	b.Children = append(b.Children, children...)
}

func BuildTree[T comparable, N HzmTree[T]](nodes []N) []N {
	if nodes == nil {
		return nil
	}

	notRoots := streams.OfSlice(nodes).Filter(isNotRoot[T, N]).ToSlice()
	parentId2NodesMap := streams.GroupingByIdentity(notRoots, func(node N) T {
		return node.GetParentId()
	})

	roots := streams.OfSlice(nodes).Filter(isRoot[T, N]).ToSlice()
	fillTree(roots, parentId2NodesMap)
	return roots
}

func isRoot[T comparable, N HzmTree[T]](node N) bool {
	return reflect.ValueOf(N.GetParentId(node)).IsZero()
}

func isNotRoot[T comparable, N HzmTree[T]](node N) bool {
	return !isRoot[T](node)
}

func fillTree[T comparable, N HzmTree[T]](nodes []N, parentId2NodesMap map[T][]N) {
	for _, node := range nodes {
		id := N.GetId(node)
		if children, ok := parentId2NodesMap[id]; ok {
			// 这里新建一个切片主要是为了将children的元素类型转化为HzmTree[T]类型
			// 没办法，因为go语言不像Java，切片类型无法自动转换过去，即使元素类型一致
			//childrenSlice := make([]HzmTree[T], 0, len(children))
			//for _, child := range children {
			//	childrenSlice = append(childrenSlice, child)
			//}
			//HzmTree[T].AddChildren(node, childrenSlice)

			for _, child := range children {
				// 上面的问题可以通过可变参数传参的方式解决
				HzmTree[T].AddChildren(node, child)
			}
			fillTree(children, parentId2NodesMap)
		}
	}
}

//func fillTree[T comparable](nodes []HzmTree[T], parentId2NodesMap map[T][]HzmTree[T]) {
//	for _, node := range nodes {
//		if children, ok := parentId2NodesMap[node.GetId()]; ok {
//			node.AddChildren(children)
//			fillTree(children, parentId2NodesMap)
//		}
//	}
//}
